package com.study.util.crypt;

import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * 常用加密解密工具集
 * created by fangming
 */
public class EncryptUtil {


    /**
     * MD5
     * 单向加密
     * 把一个任意长度的字节串变换成一定长的十六进制数字串
     */
    public static byte[] MD5(String info) throws NoSuchAlgorithmException {
        //根据MD5算法生成MessageDigest对象
        MessageDigest md5 = MessageDigest.getInstance("MD5");
        byte[] srcBytes = info.getBytes();
        //使用srcBytes更新摘要
        md5.update(srcBytes);
        //完成哈希计算，得到result
        byte[] resultBytes = md5.digest();
        return resultBytes;
    }

    public static String encryMd5(String info) {
        try {
            return bytesConvertToHexString(MD5(info));
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return "";
    }


    /**
     * Base64
     * 用commons-codec的Base64不会导致 换行
     */
    public static String BASE64Encode(byte[] bytes) {
        return Base64.encodeBase64String(bytes);
    }

    /**
     * @param info
     * @return
     */
    public static byte[] BASE64Decode(String info) {
        byte[] bt = Base64.decodeBase64(info);
        return bt;
    }

    /**
     * SHA
     * 最安全的散列算法之一
     * 接收一段明文,以一种不可逆的方式将它转换成一段（通常更小）密文
     */
    public static byte[] SHA(String info) throws NoSuchAlgorithmException {
        MessageDigest sha = MessageDigest.getInstance("SHA-256");
        byte[] srcBytes = info.getBytes();
        //使用srcBytes更新摘要
        sha.update(srcBytes);
        //完成哈希计算，得到result
        byte[] resultBytes = sha.digest();
        return resultBytes;
    }

    public static String encrySha(String info) {
        try {
            return bytesConvertToHexString(SHA(info));
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * SHA-1
     */
    public static byte[] SHA1(String info) throws NoSuchAlgorithmException {
        MessageDigest sha = MessageDigest.getInstance("SHA-256");
        byte[] srcBytes = info.getBytes();
        //使用srcBytes更新摘要
        sha.update(srcBytes);
        //完成哈希计算，得到result
        byte[] resultBytes = sha.digest();
        return resultBytes;
    }

    public static String encrySha1(String info) {
        try {
            return bytesConvertToHexString(SHA1(info));
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return "";
    }

    public static String bytesConvertToHexString(byte[] bytes) {
        StringBuffer sb = new StringBuffer();
        for (byte aByte : bytes) {
            String s = Integer.toHexString(0xff & aByte);
            if (s.length() == 1) {
                sb.append("0" + s);
            } else {
                sb.append(s);
            }
        }
        return sb.toString();
    }

    /**
     * 银联的加密方式
     */
    public static byte[] encryptDES_ECB8(byte[] bytes, String password) throws Exception {
        byte[] pwd1 = hexStr2BinArr(password.substring(0, 16));
        byte[] pwd2 = hexStr2BinArr(password.substring(16, 32));
        byte[] once = encryptDES(bytes, pwd1);
        byte[] twice = decryptDES(once, pwd2);
        byte[] third = encryptDES(twice, pwd1);
        return third;
    }

    public static byte[] decryptDES_ECB8(byte[] bytes, String password) throws Exception {
        byte[] pwd1 = hexStr2BinArr(password.substring(0, 16));
        byte[] pwd2 = hexStr2BinArr(password.substring(16, 32));
        byte[] once = decryptDES(bytes, pwd1);
        byte[] twice = encryptDES(once, pwd2);
        byte[] third = decryptDES(twice, pwd1);
        return third;
    }


    //    public static byte[] encryptDES(byte[] bytes, byte[] password) throws Exception {
//        // DES算法要求有一个可信任的随机数源
//        // 从原始密匙数据创建DESKeySpec对象
//        DESKeySpec dks = new DESKeySpec(password);
//        // 创建一个密匙工厂，然后用它把DESKeySpec转换成一个SecretKey对象
//        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
//        SecretKey secretKey = keyFactory.generateSecret(dks);
//        // Cipher对象实际完成加密操作
//        Cipher cipher = Cipher.getInstance("DES");
//        // 用密匙初始化Cipher对象
//        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
//        // 执行加密操作
//        return cipher.doFinal(bytes);
//    }
    public static byte[] encryptDES(byte[] byteS, byte[] key) {
        byte[] byteFina = null;
        try {
            Cipher cipher = Cipher.getInstance("DES/ECB/NoPadding");
            DESKeySpec desKeySpec = new DESKeySpec(key);
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
            SecretKey secretKey = keyFactory.generateSecret(desKeySpec);
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
            byteFina = cipher.doFinal(byteS);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return byteFina;
    }

    //    public static byte[] decryptDES(byte[] bytes, byte[] password) throws Exception {
//        // 创建一个DESKeySpec对象
//        DESKeySpec desKey = new DESKeySpec(password);
//        // 创建一个密匙工厂
//        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
//        // 将DESKeySpec对象转换成SecretKey对象
//        SecretKey securekey = keyFactory.generateSecret(desKey);
//        // Cipher对象实际完成解密操作
//        Cipher cipher = Cipher.getInstance("DES");
//        // 用密匙初始化Cipher对象
//        cipher.init(Cipher.DECRYPT_MODE, securekey);
//        // 真正开始解密操作
//        return cipher.doFinal(bytes);
//    }
    public static byte[] afterFillingWithSpace(byte[] orgi, int lastLength) {
        if (orgi == null)
            return null;
        byte[] data = new byte[lastLength];
        int len = orgi.length > lastLength ? lastLength : orgi.length;
        for (int i = 0; i < len; i++) {
            data[i] = orgi[i];
        }
        for (int i = len; i < lastLength; i++)
            data[i] = '\0';
        return data;
    }

    public static byte[] decryptDES(byte[] byteS, byte[] key) {
        byte[] byteFina = null;
        try {
            Cipher cipher = Cipher.getInstance("DES/ECB/NoPadding");
            DESKeySpec desKeySpec = new DESKeySpec(key);
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
            SecretKey secretKey = keyFactory.generateSecret(desKeySpec);
            cipher.init(Cipher.DECRYPT_MODE, secretKey);
            byteFina = cipher.doFinal(byteS);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return byteFina;
    }

    public static String decryptRemoveSpace(byte[] data, byte[] key) {
        return new String(decryptDES(data, key), Charset.forName("GBK")).trim();
    }
//    public static String encryDes(String info, String pwd) {
//        try {
//            return bytesConvertToHexString(encryptDES(info.getBytes(), pwd));
//        } catch (NoSuchAlgorithmException e) {
//            e.printStackTrace();
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//        return "";
//    }

//    public static String decryDes(String info, String pwd) {
//        try {
//            return bytesConvertToHexString(decryptDES(info.getBytes(), pwd));
//        } catch (NoSuchAlgorithmException e) {
//            e.printStackTrace();
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//        return "";
//    }

    /**
     * 3DES
     * 3DES又称Triple encryptDES，是DES加密算法的一种模式
     * 它使用3条56位的密钥对3DES,更安全
     */
    public static byte[] encrypt3DES(byte[] src, byte[] password) {
        try {
            SecretKey deskey = new SecretKeySpec(password, "DESede");    //生成密钥
            Cipher c1 = Cipher.getInstance("DESede");    //实例化负责加密/解密的Cipher工具类
            c1.init(Cipher.ENCRYPT_MODE, deskey);    //初始化为加密模式
            return c1.doFinal(src);
        } catch (NoSuchAlgorithmException e1) {
            e1.printStackTrace();
        } catch (javax.crypto.NoSuchPaddingException e2) {
            e2.printStackTrace();
        } catch (Exception e3) {
            e3.printStackTrace();
        }
        return null;
    }

    public static String encrypt3DES(String original, byte[] password) {
        try {
            SecretKey deskey = new SecretKeySpec(password, "DESede");    //生成密钥
            Cipher c1 = Cipher.getInstance("DESede");    //实例化负责加密/解密的Cipher工具类
            c1.init(Cipher.ENCRYPT_MODE, deskey);    //初始化为加密模式
            byte[] encryptArr = c1.doFinal(original.getBytes());
            return EncryptUtil.BASE64Encode(encryptArr);
        } catch (NoSuchAlgorithmException e1) {
            e1.printStackTrace();
        } catch (javax.crypto.NoSuchPaddingException e2) {
            e2.printStackTrace();
        } catch (Exception e3) {
            e3.printStackTrace();
        }
        return null;
    }

    public static byte[] decrypt3DES(byte[] src, byte[] password) {
        try {
            SecretKey deskey = new SecretKeySpec(password, "DESede");
            Cipher c1 = Cipher.getInstance("DESede");
            c1.init(Cipher.DECRYPT_MODE, deskey);    //初始化为解密模式
            return c1.doFinal(src);

        } catch (NoSuchAlgorithmException e1) {
            e1.printStackTrace();

        } catch (javax.crypto.NoSuchPaddingException e2) {
            e2.printStackTrace();

        } catch (Exception e3) {
            e3.printStackTrace();

        }
        return null;

    }

    /**
     * 3DES的密钥必须是24位的byte数组(采用第③种)<br>
     * 构造24位byte数组的方法:<br>
     * ①按密钥固定长度重新定义字符串<br>
     * ②先把字符串用Base64或者MD5加密，然后截取固定长度的字符转成byte数组<br>
     * ③字符串转成Byte数组，针对该数组进行修改，若长度过长则只截取一部分，若长度不够则补零<br>
     *
     * @param keyStr
     * @return
     * @throws java.io.UnsupportedEncodingException
     */
    public static byte[] build3DesKey(String keyStr) throws UnsupportedEncodingException {
        byte[] key = new byte[24];    //声明一个24位的字节数组，默认里面都是0
        byte[] temp = keyStr.getBytes("UTF-8");    //将字符串转成字节数组

        // 执行数组拷贝System.arraycopy(源数组，从源数组哪里开始拷贝，目标数组，拷贝多少位)
        if (key.length > temp.length) {
            //如果temp不够24位，则拷贝temp数组整个长度的内容到key数组中
            System.arraycopy(temp, 0, key, 0, temp.length);
        } else {
            //如果temp大于24位，则拷贝temp数组24个长度的内容到key数组中
            System.arraycopy(temp, 0, key, 0, key.length);
        }
        return key;
    }

    /**
     * DES加密,采用ECB模式,每8个字节加密一次,不足用0x00补齐
     *
     * @param str      原始待加密字符串
     * @param password DES加密密码(原文)
     * @return 返回Base64编码的加密数据
     */
    public static String encrypt_DES_ECB(String str, String password) {
        try {
            byte[] bytes = str.getBytes("GBK");
            int bs;
            if (bytes.length % 8 != 0) {
                bs = bytes.length / 8 + 1;
            } else {
                bs = bytes.length / 8;
            }
            byte[] data = afterFillingWithSpace(bytes, bs * 8);
            byte[] encrypted = encryptDES_ECB8(data, password);
            return BASE64Encode(encrypted);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * DES解密,采用ECB模式,每8个字节加密一次,不足用0x00补齐
     *
     * @param base64Str Base64编码的待解字符串
     * @param password  DES加密密码(原文)
     * @return 返回解密数据原始数据
     */
    public static String decrypt_DES_ECB(String base64Str, String password) {
        try {
            byte[] decryptData = BASE64Decode(base64Str);
            byte[] decrypted = decryptDES_ECB8(decryptData, password);
            return new String(decrypted, Charset.forName("GBK")).trim();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }

//    private static String hexStr = "1234567890ABCDEF";

    //    public static byte[] hexStr2BinArr(String hexString) {
//        //hexString的长度对2取整，作为bytes的长度
//        int len = hexString.length() / 2;
//        byte[] bytes = new byte[len];
//        byte high = 0;//字节高四位
//        byte low = 0;//字节低四位
//        for (int i = 0; i < len; i++) {
//            //右移四位得到高位
//            high = (byte) ((hexStr.indexOf(hexString.charAt(2 * i))) << 4);
//            low = (byte) hexStr.indexOf(hexString.charAt(2 * i + 1));
//            bytes[i] = (byte) (high | low);//高地位做或运算
//        }
//        return hexString.getBytes();
//    }
    public static byte[] hexStr2BinArr(String hexString) {
        hexString = hexString.toUpperCase();
        int length = hexString.length() / 2;
        char[] hexChars = hexString.toCharArray();
        byte[] d = new byte[length];
        for (int i = 0; i < length; i++) {
            int pos = i << 1;
            d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
        }
        return d;
    }

    private static byte charToByte(char c) {
        return (byte) "0123456789ABCDEF".indexOf(c);
    }

}
